/*
 * $Id: wintabwrapper.c,v 1.1 2006/05/20 07:20:36 jdfekte Exp $
 *
 * Copyright (c) 2001-2002 Pierre Dragicevic, Jean-Daniel Fekete and
 * Ecole des Mines de Nantes.  All rights reserved.
 *
 * This software is proprietary information of Pierre Dragicevic, 
 * Jean-Daniel Fekete Ecole des Mines de Nantes.  You shall use it
 * only in accordance with the terms of the license agreement you
 * accepted when downloading this software.  The license is available
 * in the file licence.txt and at the following URL:
 * http://www.emn.fr/fekete/ICon/licence.html
 */

#include <windows.h> // windows definitions for wintab
#include <wintab.h>
#include <jni.h>
#include "fr_emn_win32input_jwintab_JWintab.h"

char stringBuf[5000];
const int maxPktSize = sizeof(HCTX) + 5*sizeof(UINT) + sizeof(LONG) + sizeof(WTPKT) + 4*sizeof(DWORD) + 5*sizeof(INT);

void printLOGCONTEXT(LOGCONTEXT l) {
  printf("%s\n", l.lcName);
  printf("%d\n", l.lcOptions);
 printf("%d\n", l.lcStatus); // 1
  printf("%d\n", l.lcLocks); // 2
  printf("%d\n", l.lcMsgBase); // 3
  printf("%d\n", l.lcDevice); // 4
  printf("%d\n", l.lcPktRate); // 5
  printf("%d\n", l.lcPktData); // 6
  printf("%d\n", l.lcPktMode); // 7
  printf("%d\n", l.lcMoveMask); // 8
  printf("%d\n", l.lcBtnDnMask); // 9
  printf("%d\n", l.lcBtnUpMask); // 10
  printf("%d\n", l.lcInOrgX); // 11
  printf("%d\n", l.lcInOrgY); // 12
  printf("%d\n", l.lcInOrgZ); // 13
  printf("%d\n", l.lcInExtX); // 14
  printf("%d\n", l.lcInExtY); // 15
  printf("%d\n", l.lcInExtZ); // 16
  printf("%d\n", l.lcOutOrgX); // 17
  printf("%d\n", l.lcOutOrgY); // 18
  printf("%d\n", l.lcOutOrgZ); // 19
  printf("%d\n", l.lcOutExtX); // 20
  printf("%d\n", l.lcOutExtY); // 21
  printf("%d\n", l.lcOutExtZ); // 22
  printf("%d\n", l.lcSensX); // 23
  printf("%d\n", l.lcSensY); // 24
  printf("%d\n", l.lcSensZ); // 25
  printf("%d\n", l.lcSysMode); // 26 (int)
  printf("%d\n", l.lcSysOrgX); // 27
  printf("%d\n", l.lcSysOrgY); // 28
  printf("%d\n", l.lcSysExtX); // 29
  printf("%d\n", l.lcSysExtY); // 30
  printf("%d\n", l.lcSysSensX); // 31
  printf("%d\n", l.lcSysSensY); // 32
}

void fillLOGCONTEXT(LOGCONTEXT *l, const char *c0, jint *c1) {
  int i; 
  for (i=0; c0[i]!='\0'; i++)
    l->lcName[i]=c0[i];
  for (; i<40; i++)
    l->lcName[i]='\0';
  l->lcOptions = c1[0];  l->lcStatus = c1[1];  l->lcLocks = c1[2];
  l->lcMsgBase = c1[3];  l->lcDevice = c1[4];  l->lcPktRate = c1[5];
  l->lcPktData = c1[6];  l->lcPktMode = c1[7];  l->lcMoveMask = c1[8];
  l->lcBtnDnMask = c1[9];  l->lcBtnUpMask = c1[10];  l->lcInOrgX = c1[11];
  l->lcInOrgY = c1[12];  l->lcInOrgZ = c1[13];  l->lcInExtX = c1[14];
  l->lcInExtY = c1[15];  l->lcInExtZ = c1[16];  l->lcOutOrgX = c1[17];
  l->lcOutOrgY = c1[18];  l->lcOutOrgZ = c1[19];  l->lcOutExtX = c1[20];
  l->lcOutExtY = c1[21];  l->lcOutExtZ = c1[22];  l->lcSensX = c1[23];
  l->lcSensY = c1[24];  l->lcSensZ = c1[25];  l->lcSysMode = c1[26];
  l->lcSysOrgX = c1[27];  l->lcSysOrgY = c1[28];  l->lcSysExtX = c1[29];
  l->lcSysExtY = c1[30];  l->lcSysSensX = c1[31];  l->lcSysSensY = c1[32];
} 


char *convertPacket(char *pck, jint *p, int mask, int dataMask) {
  char *pack = pck;

  if (mask & PK_CONTEXT) {
    p[0] = (jint)*((HCTX *)pack);
    pack += sizeof(HCTX);
  } else p[0] = 0;

  if (mask & PK_STATUS) {
    p[1] = (jint)*((UINT *)pack);
    pack += sizeof(UINT);
  } else p[1] = 0;

  if (mask & PK_TIME) {
    p[2] = (jint)*((LONG *)pack);
    pack += sizeof(LONG);
  } else p[2] = 0;

  if (mask & PK_CHANGED) {
    p[3] = (jint)*((WTPKT *)pack);
    pack += sizeof(WTPKT);
  } else p[3] = 0;

  if (mask & PK_SERIAL_NUMBER) {
    p[4] = (jint)*((UINT *)pack);
    pack += sizeof(UINT);
  } else p[4] = 0;

  if (mask & PK_CURSOR) {
    p[5] = (jint)*((UINT *)pack);
    pack += sizeof(UINT);
  } else p[5] = 0;

  if (mask & PK_BUTTONS) {
    p[6] = (jint)*((DWORD *)pack);
    pack += sizeof(DWORD);
  } else p[6] = 0;

  if (mask & PK_X) {
    p[7] = (jint)*((DWORD *)pack);
    pack += sizeof(DWORD);
  } else p[7] = 0;

  if (mask & PK_Y) {
    p[8] = (jint)*((DWORD *)pack);
    pack += sizeof(DWORD);
  } else p[8] = 0;

  if (mask & PK_Z) {
    p[9] = (jint)*((DWORD *)pack);
    pack += sizeof(DWORD);
  } else p[9] = 0;

  if (mask & PK_NORMAL_PRESSURE) {
    if (dataMask & PK_NORMAL_PRESSURE) {
      p[10] = (jint)*((int *)pack);
      pack += sizeof(int);
    }
    else {
      p[10] = (jint)*((UINT *)pack);
      pack += sizeof(UINT);
    }
  } else p[10] = 0;

  if (mask & PK_TANGENT_PRESSURE) {
    if (dataMask & PK_TANGENT_PRESSURE) {
      p[11] = (jint)*((int *)pack);
      pack += sizeof(int);
    }
    else {
      p[11] = (jint)*((UINT *)pack);
      pack += sizeof(UINT);
    }
  } else p[11] = 0;

  if (mask & PK_ORIENTATION) {
    p[12] = (jint) ((*(ORIENTATION *)pack).orAzimuth);
    p[13] = (jint) ((*(ORIENTATION *)pack).orAltitude);
    p[14] = (jint) ((*(ORIENTATION *)pack).orTwist);
    pack += sizeof(ORIENTATION);
  } else p[12] = p[13] = p[14] = 0;

  return pack;
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    isWTInfoSupported
 * Signature: (II)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_isWTInfoSupported
  (JNIEnv *env, jclass class, jint category, jint index) {
  return (WTInfo(category, index, 0) != 0);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_boolean
 * Signature: (II)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1boolean
  (JNIEnv *env, jclass class, jint category, jint index) {
  boolean buf[] = {FALSE};
  WTInfo(category, index, buf);
  return buf[0];
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_byte
 * Signature: (II)B
 */
JNIEXPORT jbyte JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1byte
  (JNIEnv *env, jclass class, jint category, jint index) {
  byte buf[] = {-1};
  WTInfo(category, index, buf);
  return buf[0];
}
/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_byte32
 * Signature: (III)B
 */
JNIEXPORT jbyte JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1byte32
  (JNIEnv *env, jclass class, jint category, jint index, jint num) {
  byte buf[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
  if (num<0 || num>31)
    return -1;
  WTInfo(category, index, buf);
  return buf[num];
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_int
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1int
  (JNIEnv *env, jclass class, jint category, jint index) {
  int buf[] = {-1};
  WTInfo(category, index, buf);
  return buf[0];
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_short
 * Signature: (II)S
 */
JNIEXPORT jshort JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1short
  (JNIEnv *env, jclass class, jint category, jint index) {
  short buf[1] = {-1};
  WTInfo(category, index, buf);
  return buf[0];
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_String
 * Signature: (II)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1String
  (JNIEnv *env, jclass class, jint category, jint index) {
  char *buf = stringBuf; buf[0] = 0;
  WTInfo(category, index, buf);
  return (*env)->NewStringUTF(env, buf);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_multiString
 * Signature: (III)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1multiString
  (JNIEnv *env, jclass class, jint category, jint index, jint num) {
  int n,start,count,i;
  char *buf = stringBuf; buf[0] = 0;
  
  if (num>=0 && num<=31) {

    WTInfo(category, index, buf);
  
    // Find the beginning of the string to extract
    start = 0;
    for (count=0; count<num; count++) {
      for (; buf[start]!='\0'; start++);
      start++;
    }
    // Copy string at the beginning of the buffer
    for (i=start; buf[i]!='\0'; i++)
      buf[i-start]=buf[i];
    buf[i-start]='\0'; 
  }

  return (*env)->NewStringUTF(env, buf);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_Axis
 * Signature: (II)[I
 */
JNIEXPORT jintArray JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1Axis
  (JNIEnv *env, jclass class, jint category, jint index) {
  AXIS axis = {0, 0, 0, 0};
  jint a[4];
  jintArray ja;
 
  WTInfo(category, index, &axis);
  a[0] = axis.axMin;
  a[1] = axis.axMax;
  a[2] = axis.axUnits;
  a[3] = axis.axResolution;

  ja = (*env)->NewIntArray(env, 4);
  (*env)->SetIntArrayRegion(env, ja, 0, 4, a);
  return ja;
}


/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTInfo_Axis3
 * Signature: (II)[I
 */
JNIEXPORT jintArray JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTInfo_1Axis3
  (JNIEnv *env, jclass class, jint category, jint index, jint num) {
  AXIS axis[] = { {0,0,0,0}, {0,0,0,0}, {0,0,0,0} };
  jint a[4];
  jintArray ja;
 
  WTInfo(category, index, &axis);
  a[0] = axis[num].axMin;
  a[1] = axis[num].axMax;
  a[2] = axis[num].axUnits;
  a[3] = axis[num].axResolution;

  ja = (*env)->NewIntArray(env, 4);
  (*env)->SetIntArrayRegion(env, ja, 0, 4, a);
  return ja;
}


/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTOpen
 */
JNIEXPORT jint JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTOpen
  (JNIEnv *env, jclass class, jint hwnd, jstring ctx0, jintArray ctx1, jboolean enable) {
  LOGCONTEXT l;
  const char *c0 = (*env)->GetStringUTFChars(env, ctx0, 0);
  jint *c1 = (*env)->GetIntArrayElements(env, ctx1, 0);
  jint hctx;

  fillLOGCONTEXT(&l, c0, c1);

  (*env)->ReleaseStringUTFChars(env, ctx0, c0);
  (*env)->ReleaseIntArrayElements(env, ctx1, c1, 0);

//  printLOGCONTEXT(l);

  return (jint)WTOpen((void*)hwnd, &l, enable);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTClose
 * Signature: (I)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTClose
  (JNIEnv *env, jclass class, jint hctx) {
  return WTClose((void*)hctx);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTGetName
 * Signature: (I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTGetName
  (JNIEnv *env, jclass class, jint hctx) {
  LOGCONTEXT l;
  WTGet((void*)hctx, &l);
  return (*env)->NewStringUTF(env, l.lcName);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTGet
 * Signature: (I)[I
 */
JNIEXPORT jintArray JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTGet
  (JNIEnv *env, jclass class, jint hctx) {
  LOGCONTEXT l;
  jintArray a;
  if (WTGet((void*)hctx, &l)) {
    jint c1[] = { l.lcOptions, l.lcStatus, l.lcLocks, l.lcMsgBase,
		  l.lcDevice, l.lcPktRate, l.lcPktData, l.lcPktMode,
		  l.lcMoveMask, l.lcBtnDnMask, l.lcBtnUpMask, l.lcInOrgX,
		  l.lcInOrgY, l.lcInOrgZ, l.lcInExtX, l.lcInExtY, l.lcInExtZ,
		  l.lcOutOrgX, l.lcOutOrgY, l.lcOutOrgZ, l.lcOutExtX,
		  l.lcOutExtY, l.lcOutExtZ, l.lcSensX, l.lcSensY, l.lcSensZ,
		  l.lcSysMode, l.lcSysOrgX, l.lcSysOrgY, l.lcSysExtX,
		  l.lcSysExtY, l.lcSysSensX, l.lcSysSensY };
    a = (*env)->NewIntArray(env, 33);
    (*env)->SetIntArrayRegion(env, a, 0, 32, c1);
  }
  return a;
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTSet
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTSet
  (JNIEnv *env, jclass class, jint hctx, jstring ctx0, jintArray ctx1) {
  LOGCONTEXT l;
  const char *c0 = (*env)->GetStringUTFChars(env, ctx0, 0);
  jint *c1 = (*env)->GetIntArrayElements(env, ctx1, 0);

  fillLOGCONTEXT(&l, c0, c1);

  (*env)->ReleaseStringUTFChars(env, ctx0, c0);
  (*env)->ReleaseIntArrayElements(env, ctx1, c1, 0);

  return WTSet((void*)hctx, &l);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTEnable
 * Signature: (IZ)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTEnable
  (JNIEnv *env, jclass class, jint hctx, jboolean enable) {
  return WTEnable((void*)hctx, enable);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTOverlap
 * Signature: (IZ)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTOverlap
  (JNIEnv *env, jclass class, jint hctx, jboolean top) {
  return WTOverlap((void*)hctx, top);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTPacketsGet
 */
JNIEXPORT jintArray JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTPacketsGet
  (JNIEnv *env, jclass class, jint hctx, jint maxpckts, jint mask, jint dataMask) {
  char *packs, *pack; // it is declared char so that we can iterate
  jint *p;
  int n, i, j;
  jintArray a;

  // gets packets
  packs = malloc(maxpckts * maxPktSize);
  n = WTPacketsGet((void*)hctx, maxpckts, packs);

  if (n>0) { 
    // translate packets into fixed size int arrays (15 elements)
    p = (jint *)malloc(n * (sizeof(jint)*15) );
    pack = packs;
    for (i=0, j=0; i<n; i++) {
      pack = convertPacket(pack, &p[j], mask, dataMask);
      j += 15;
    }
    
    a = (*env)->NewIntArray(env, n*15);
    (*env)->SetIntArrayRegion(env, a, 0, n*15, p);
    free(p);
    free(packs);
    return a;
  }
 
  free(packs);
  return NULL;
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTGetLastPacket
 * Signature: (III)[I
 */
JNIEXPORT jintArray JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTGetLastPacket
  (JNIEnv *env, jclass class, jint hctx, jint mask, jint dataMask) {
  int old, new;
  char *pack = malloc(maxPktSize);
  jint p[15];
  jintArray a;

  // Retreive the serial numbers of the oldest and newest packets
  WTQueuePacketsEx((void *)hctx, &old, &new);
  // Retreive the newest packet
  if (WTPacket((void *)hctx, new, pack) > 0) {
    convertPacket(pack, p, mask, dataMask);

    a = (*env)->NewIntArray(env, 15);
    (*env)->SetIntArrayRegion(env, a, 0, 15, p);

    free(pack);
    return a;
  }

  free(pack);
  return NULL;
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTQueueSizeGet
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTQueueSizeGet
  (JNIEnv *env, jclass class, jint hctx) {
  return WTQueueSizeGet((void *)hctx);
}

/*
 * Class:     fr_emn_win32input_jwintab_JWintab
 * Method:    WTQueueSizeSet
 * Signature: (II)Z
 */
JNIEXPORT jboolean JNICALL Java_fr_emn_win32input_jwintab_JWintab_WTQueueSizeSet
  (JNIEnv *env, jclass class, jint hctx, jint size) {
  return WTQueueSizeSet((void *)hctx, size);
}




















